#include "keyboard.h"
#include "ui_keyboard.h"
#include <map>
#include <QStringListModel>
#include <QDesktopWidget>
#include <QScrollBar>
#include <QDebug>
#include <QDBusInterface>
#include <QDBusReply>
#include <QScroller>
#include <qalgorithms.h>
#include <QX11Info>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/extensions/XTest.h>


#define KEYSYM_UP 0xff52
#define KEYSYM_LEFT 0xff51
#define KEYSYM_RIGHT 0xff53
#define KEYSYM_DOWN 0xff54
#define KEYSYM_BACKSPACE 0xff08
#define KEYSYM_RETURN 0xff0d

#define CH_CHAR_VIEW_SIZE 9
#define KBD_ROW_SIZE 10

#define FCITX_SERVICE "org.fcitx.Fcitx"
#define FCITX_IM_PATH "/inputmethod"
#define FCITX_IM_INF "org.fcitx.Fcitx.InputMethod"

enum _INPUT_RETURN_VALUE {
    IRV_TO_PROCESS = 0, /* do something */
    IRV_FLAG_BLOCK_FOLLOWING_PROCESS = 1 << 0, /* nothing to do, actually non-zero is blocking, but you need a flag fo
r do nothing */
    IRV_FLAG_FORWARD_KEY = 1 << 1, /* the key will be forwarded */
    IRV_FLAG_RESET_INPUT = 1 << 2, /* reset input */
    IRV_FLAG_PENDING_COMMIT_STRING = 1 << 3, /* there is something in input strStringGet buffer, commit it */
    IRV_FLAG_UPDATE_INPUT_WINDOW = 1 << 4, /* something updated in input window, let the UI update */
    IRV_FLAG_UPDATE_CANDIDATE_WORDS = 1 << 5, /* update the candidate words */
    IRV_FLAG_ENG = 1 << 6, /* special */
    IRV_FLAG_PUNC = 1 << 7, /* special */
    IRV_FLAG_DISPLAY_LAST = 1 << 8, /* special */
    IRV_FLAG_DO_PHRASE_TIPS = 1 << 9, /* special */
    /* compatible */
    IRV_DONOT_PROCESS = IRV_FLAG_FORWARD_KEY,
    IRV_COMMIT_STRING = IRV_FLAG_PENDING_COMMIT_STRING | IRV_FLAG_DO_PHRASE_TIPS,
    IRV_DO_NOTHING = IRV_FLAG_BLOCK_FOLLOWING_PROCESS,
    IRV_CLEAN = IRV_FLAG_RESET_INPUT,
    IRV_COMMIT_STRING_REMIND = IRV_FLAG_PENDING_COMMIT_STRING | IRV_FLAG_UPDATE_INPUT_WINDOW,
    IRV_DISPLAY_CANDWORDS = IRV_FLAG_UPDATE_INPUT_WINDOW | IRV_FLAG_UPDATE_CANDIDATE_WORDS,
    IRV_DONOT_PROCESS_CLEAN = IRV_FLAG_FORWARD_KEY | IRV_FLAG_RESET_INPUT,
    IRV_COMMIT_STRING_NEXT =  IRV_FLAG_PENDING_COMMIT_STRING | IRV_FLAG_UPDATE_INPUT_WINDOW,
    IRV_DISPLAY_MESSAGE = IRV_FLAG_UPDATE_INPUT_WINDOW,
    IRV_ENG = IRV_FLAG_PENDING_COMMIT_STRING | IRV_FLAG_ENG | IRV_FLAG_RESET_INPUT,
    IRV_PUNC = IRV_FLAG_PENDING_COMMIT_STRING | IRV_FLAG_PUNC | IRV_FLAG_RESET_INPUT,
    IRV_DISPLAY_LAST = IRV_FLAG_UPDATE_INPUT_WINDOW | IRV_FLAG_DISPLAY_LAST
};

extern std::map<QString, QString> pinyin;

namespace { //anymouse namespace start

enum InputType {
    E_ZH_JT, //简体中文
    E_ZH_SYM, //中文标点符号和数字
    E_EN_LOWER, //英文字母小写
    E_EN_UPPER, //英文字母大写
    E_EN_SYM, //英文标点符号和数字
    E_ITYPE_LAST
};
enum InputStatus {
    E_INPUTING,
    E_SELECTING,
    //E_Closed
};

InputType iType = E_ZH_JT;
InputStatus iStatus = E_INPUTING;
//第i页第j个按钮的layout index
int curBtnIndexes[E_ITYPE_LAST];
QList<QPushButton*>pageButtons[E_ITYPE_LAST];
QStringListModel *model = nullptr;
QStringList chChars;
QStringList emptyStrs = {"", "", "", "", "", "", "", ""}; //to align QListView Model
int chIdx = -1;
int validChars = 0;
KeyCode kcBackSpace, kcReturn; //删除、回车键键值
Display *x11dpy = nullptr;
QDesktopWidget *desktop = nullptr;
QDBusInterface *fcitxInf = nullptr;
bool enterBtnClicked = false; 
bool handlingKeyRet = false;

void sendKey(KeyCode kcode) {
    XTestGrabControl(x11dpy, True);
    XTestFakeKeyEvent(x11dpy, kcode, True, CurrentTime);
    XTestFakeKeyEvent(x11dpy, kcode, False, CurrentTime);
    XTestGrabControl(x11dpy, False);
    XFlush(x11dpy);
}

} //anymouse namespace end

Keyboard::Keyboard(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Keyboard)
{
    setWindowFlags(Qt::BypassWindowManagerHint|Qt::WindowStaysOnTopHint);
    ui->setupUi(this);
    ui->kbdStack->setCurrentIndex(iType);
    QScroller::grabGesture(ui->chchars, QScroller::LeftMouseButtonGesture);
    for(auto i=0; i<E_ITYPE_LAST; i++) {
        auto w = ui->kbdStack->widget(i);
        pageButtons[i] = w->findChildren<QPushButton*>();
        std::sort(pageButtons[i].begin(), pageButtons[i].end(),
                  [w](QPushButton *a, QPushButton *b) {
            auto ly = qobject_cast<QGridLayout*>(w->layout());
            int r1, c1, r2, c2, sr, sc;
            ly->getItemPosition(ly->indexOf(a), &r1, &c1, &sr, &sc);
            ly->getItemPosition(ly->indexOf(b), &r2, &c2, &sr, &sc);

            return r1<r2 || (r1==r2 &&c1<c2);
        });
        curBtnIndexes[i] = 0;
        pageButtons[i][0]->setFlat(true);
    }
    x11dpy = QX11Info::display();
    desktop = qApp->desktop();
    kcBackSpace = XKeysymToKeycode(x11dpy, XK_BackSpace);
    kcReturn = XKeysymToKeycode(x11dpy, XK_Return);
    fcitxInf = new QDBusInterface(FCITX_SERVICE, FCITX_IM_PATH, FCITX_IM_INF,
                                  QDBusConnection::sessionBus(), this);

    kbdBtnSlots = {
        {"hide", &Keyboard::onHideBtnClicked},
        {"隐藏", &Keyboard::onHideBtnClicked},
        {"return", &Keyboard::onReturnBtnClicked},
        {"返回", &Keyboard::onReturnBtnClicked},
        {"enter", &Keyboard::onEnterBtnClicked},
        {"回车", &Keyboard::onEnterBtnClicked},
        {"删除", &Keyboard::onDeleteBtnClicked},
        {"delete", &Keyboard::onDeleteBtnClicked},
        {"&&123", &Keyboard::onSymbolBtnClicked},
        {"中", &Keyboard::onLanguageBtnClicked},
        {"En", &Keyboard::onLanguageBtnClicked},
        {"&&", &Keyboard::onAndBtnClicked},
        {"space", &Keyboard::onSpaceBtnClicked},
        {"空格", &Keyboard::onSpaceBtnClicked},
        {"capslock", &Keyboard::onCapsBtnClicked},
        {"CapsLock", &Keyboard::onCapsBtnClicked}
    };
    model = new QStringListModel(this);
    ui->chchars->setModel(model);
    bindSignal();
    onDesktopResized();
}

Keyboard::~Keyboard()
{
    delete ui;
}

void Keyboard::bindSignal() {
    auto btns = ui->kbdStack->findChildren<QPushButton*>();
    for(auto btn: btns){
        auto it = kbdBtnSlots.find(btn->text());
        connect(btn, &QPushButton::clicked, this,
                it==kbdBtnSlots.end() ?&Keyboard::onTextBtnClicked: it->second);
    }
    connect(desktop, &QDesktopWidget::resized, this, &Keyboard::onDesktopResized);
    connect(ui->chchars, &QListView::clicked, [this](const QModelIndex &index){
        ui->chchars->setCurrentIndex(index);
        chIdx = index.row();
        if(chIdx < validChars) {
            Q_EMIT Commit(chChars[chIdx]);
            iStatus = E_INPUTING;
            model->setStringList(QStringList());
            validChars = -1;
            ui->py->setText("");
            pageButtons[iType][curBtnIndexes[iType]]->setFlat(true);
        }
    });
}

void Keyboard::onTextBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    if(iType == E_ZH_JT && btn->text().isLower()) {
        if(ui->py->text().endsWith(btn->text())) {
            if(validChars>0) {
                 iStatus = E_SELECTING;
                 chIdx = 0;
                 ui->chchars->setCurrentIndex(model->index(chIdx));
                 btn->setFlat(false);
            }
            return;
        }
        ui->py->setText(ui->py->text()+btn->text());
        auto it = pinyin.find(ui->py->text());
        if(it == pinyin.end()) {
            chChars.clear();
        } else {
            chChars = it->second.split("", QString::SkipEmptyParts);
        }
        validChars = chChars.size();
        //用空字符补齐9个汉字
        auto r = CH_CHAR_VIEW_SIZE-chChars.size()%CH_CHAR_VIEW_SIZE;
        if(r<CH_CHAR_VIEW_SIZE) chChars << emptyStrs.mid(0, r);
        model->setStringList(chChars); //QString
        //ui->chchars->setCurrentIndex(QModelIndex());
    }else {
        Q_EMIT Commit(btn->text());
    }

}
void Keyboard::onHideBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    hide();
}
void Keyboard::onReturnBtnClicked() { //返回按钮
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    if(iType==E_ZH_SYM) {
        iType = E_ZH_JT;
    }else if(iType==E_EN_SYM) {
        iType = E_EN_LOWER; //这里简单处理：统一返回小写界面
    }
    ui->kbdStack->setCurrentIndex(iType);
}
void Keyboard::onEnterBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    if(handlingKeyRet) {
        enterBtnClicked = true;
    }else {
        sendKey(kcReturn);
    }
}

void Keyboard::onDeleteBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    if(iStatus==E_INPUTING && !ui->py->text().isEmpty()) {
        ui->py->setText(ui->py->text().mid(0, ui->py->text().size()-1));
        auto it = pinyin.find(ui->py->text());
        if(it==pinyin.end()) {
             chChars.clear();
        }else {
             chChars = it->second.split("", QString::SkipEmptyParts);
        }
        validChars = chChars.size();
        //用空字符补齐9个汉字
        auto r = CH_CHAR_VIEW_SIZE-chChars.size()%CH_CHAR_VIEW_SIZE;
        if(r<CH_CHAR_VIEW_SIZE) chChars << emptyStrs.mid(0, r);
        model->setStringList(chChars);
    }else {
        sendKey(kcBackSpace);
    }
}
void Keyboard::onSymbolBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    if(iType == E_ZH_JT) {
        iType = E_ZH_SYM;
    }else if(iType==E_EN_LOWER || iType==E_EN_UPPER) {
        iType = E_EN_SYM;
    }
    ui->kbdStack->setCurrentIndex(iType);
}
void Keyboard::onLanguageBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    iType = iType==E_ZH_JT ?E_EN_LOWER: E_ZH_JT;
    ui->kbdStack->setCurrentIndex(iType);
}
void Keyboard::onAndBtnClicked(){
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    Q_EMIT Commit("&");
}
void Keyboard::onCapsBtnClicked() {
    iType = iType==E_EN_LOWER ?E_EN_UPPER: E_EN_LOWER;
    ui->kbdStack->setCurrentIndex(iType);
}
void Keyboard::onSpaceBtnClicked() {
    auto btn = qobject_cast<QPushButton*>(sender());
    checkSender(btn);
    Q_EMIT Commit(" ");
}


uint Keyboard::DoInput(uint keysym){
    switch(keysym) {
    case KEYSYM_LEFT: return handleKeyLeft();
    case KEYSYM_RIGHT: return handleKeyRight();
    case KEYSYM_UP: return handleKeyUp();
    case KEYSYM_DOWN: return handleKeyDown();
    case KEYSYM_RETURN: return handleKeyReturn();
    default: break;
    }
    return IRV_TO_PROCESS;
};

uint Keyboard::handleKeyLeft() {
    if(iStatus == E_SELECTING) {
        if(chIdx>0) {
            ui->chchars->setCurrentIndex(model->index(--chIdx));
            if(chIdx%CH_CHAR_VIEW_SIZE == CH_CHAR_VIEW_SIZE-1) {
                auto val = chIdx/CH_CHAR_VIEW_SIZE*CH_CHAR_VIEW_SIZE*ui->chchars->gridSize().width();
                ui->chchars->horizontalScrollBar()->setValue(val);
            }
        }
    } else {
        auto &idx = curBtnIndexes[iType];
        if(idx>0) {
            auto btn = pageButtons[iType][idx];
            btn->setFlat(false); //old
            btn = pageButtons[iType][--idx];
            btn->setFlat(true); //new
        }
    }
    return IRV_DO_NOTHING;
}

uint Keyboard::handleKeyRight(){
    if(iStatus == E_SELECTING) {
      if(chIdx+1<validChars) {
            ui->chchars->setCurrentIndex(model->index(++chIdx));
            if(chIdx%CH_CHAR_VIEW_SIZE == 0) {
                auto val = chIdx/CH_CHAR_VIEW_SIZE*CH_CHAR_VIEW_SIZE*ui->chchars->gridSize().width();
                ui->chchars->horizontalScrollBar()->setValue(val);
            }
      }
    } else {
        auto &idx = curBtnIndexes[iType];
        if(idx+1<pageButtons[iType].size()) {
            auto btn = pageButtons[iType][idx];
            btn->setFlat(false); //old
            btn = pageButtons[iType][++idx];
            btn->setFlat(true); //new
        }
    }
    return IRV_DO_NOTHING;
}
uint Keyboard::handleKeyUp(){
    auto &idx = curBtnIndexes[iType];

    auto btn = pageButtons[iType][idx];
    if(iStatus == E_SELECTING) { //选择汉字： 翻页
        chIdx /= CH_CHAR_VIEW_SIZE;
        if(chIdx>0) chIdx--;
        chIdx *= CH_CHAR_VIEW_SIZE;

        ui->chchars->setCurrentIndex(model->index(chIdx));
        ui->chchars->horizontalScrollBar()->setValue(chIdx*ui->chchars->gridSize().width());
    }else if(idx>=KBD_ROW_SIZE) {
        btn->setFlat(false); //old
        idx -= KBD_ROW_SIZE;
        btn = pageButtons[iType][idx];
        btn->setFlat(true); //new
    }
    return IRV_DO_NOTHING;
}
uint Keyboard::handleKeyDown(){
    auto &idx = curBtnIndexes[iType];
    auto btn = pageButtons[iType][idx];
    if(iStatus == E_SELECTING) { //选择汉字： 翻页
        if(chIdx/CH_CHAR_VIEW_SIZE < validChars/CH_CHAR_VIEW_SIZE) {
            chIdx = (chIdx/CH_CHAR_VIEW_SIZE+1)*CH_CHAR_VIEW_SIZE;
            ui->chchars->setCurrentIndex(model->index(chIdx));
            ui->chchars->horizontalScrollBar()->setValue(chIdx*ui->chchars->gridSize().width());
        }
    }else if(idx/KBD_ROW_SIZE < pageButtons[iType].size()/KBD_ROW_SIZE) {
        btn->setFlat(false);
        idx += KBD_ROW_SIZE;
        if(idx>=pageButtons[iType].size()) idx = pageButtons[iType].size()-1;
        btn = pageButtons[iType][idx];
        btn->setFlat(true);
    }
    return IRV_DO_NOTHING;
}
uint Keyboard::handleKeyReturn(){
    auto &idx = curBtnIndexes[iType];
    auto btn = pageButtons[iType][idx];
    handlingKeyRet = true;
    if(iStatus == E_SELECTING) {
        Q_EMIT Commit(chChars[chIdx]);
        iStatus = E_INPUTING;
        model->setStringList(QStringList());
        ui->py->setText("");
        btn->setFlat(true);
    }else {
        btn->click();
    }
    handlingKeyRet = false;
    if(enterBtnClicked) {
        enterBtnClicked = false;
        return IRV_TO_PROCESS;
    }
    return IRV_DO_NOTHING;
}



void Keyboard::resizeEvent(QResizeEvent *e) {
    Q_UNUSED(e);
    ui->chchars->setGridSize(QSize(ui->chchars->width()/CH_CHAR_VIEW_SIZE, ui->chchars->height()));

}
void Keyboard::ShowKeyboard() {
    QDBusReply<QString> cim = fcitxInf->call("GetCurrentIM");
    if(cim.value() == "arrowkbd") show();
    else hide(); //规避“切换到其他输入法时会触发Reset->ShokKeyboard”的情况
}

void Keyboard::HideKeyboard() {
    hide();
}
void Keyboard::onDesktopResized() {
    //origin kbd: w:500 h:200
    auto dw = desktop->width();
    auto dh = desktop->height();
    int w, h;
    if(dw<dh) {
       w = dw;
       h = w*2/5;
    }else {
       w = dh;
       h = w*2/5;
    }
    setGeometry((dw-w)/2, dh-h, w, h);
}
void Keyboard::checkSender(QPushButton *btn) {
    if(pageButtons[iType][curBtnIndexes[iType]] != btn) {
        pageButtons[iType][curBtnIndexes[iType]]->setFlat(false);
        curBtnIndexes[iType] = pageButtons[iType].indexOf(btn);
        btn->setFlat(true);
    }
}

void Keyboard::SetCursorRect(int x, int y, int w, int h){
    Q_UNUSED(x); Q_UNUSED(y); Q_UNUSED(w); Q_UNUSED(h); //暂时不处理
}

